package com.hwn.datax.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.creator.druid.DruidDataSourceCreator;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.hwn.datax.domain.RepositoryConfig;
import com.hwn.datax.domain.RepositorySource;
import com.hwn.datax.service.ConfigService;
import com.hwn.datax.service.RepositoryConfigService;
import com.hwn.datax.service.RepositorySourceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.sql.*;
import java.util.*;

/**
 * @author Administrator
 */
@Service
@Slf4j
public class RepositorySourceServiceImpl implements RepositorySourceService {

    private ConfigService configService;

    private final static String CONFIG_KEY = "RepositorySourceTable";

    @Autowired
    public RepositorySourceServiceImpl(ConfigService configService) {
        this.configService = configService;
        JSONObject config = configService.getConfig(CONFIG_KEY);
        if (config != null) {
            config.forEach((k, v) -> {
                if (v instanceof JSONObject) {
                    RepositorySource repositorySource = JSONUtil.toBean((JSONObject) v, RepositorySource.class);
                    repositorySourceMap.put(Long.valueOf(k), repositorySource);
                }
            });
        }
    }

    private final static Map<Long, RepositorySource> repositorySourceMap = new HashMap<>();


    @Autowired
    DataSource dataSource;

    @Autowired
    DruidDataSourceCreator dataSourceCreator;

    @Autowired
    RepositoryConfigService repositoryConfigService;

    RepositorySource getById(Long id) {
        return repositorySourceMap.get(id);
    }


    /**
     * 保存数据连接
     *
     * @param repositorySource
     */
    @Override
    public void save(RepositorySource repositorySource) {
        if (repositorySource.getId() == null) {
            repositorySource.setId(IdUtil.getSnowflakeNextId());
        }
        repositorySourceMap.put(repositorySource.getId(), repositorySource);
        configService.saveConfig(CONFIG_KEY, JSONUtil.toJsonStr(JSONUtil.parse(repositorySourceMap), 4));
    }

    /**
     * 校验连接
     *
     * @param repositorySource
     * @return
     */
    @Override
    public Boolean testConnection(RepositorySource repositorySource) {
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        BeanUtils.copyProperties(repositorySource, dataSourceProperty);
        try {
            DataSource ds = dataSourceCreator.createDataSource(dataSourceProperty);
            DruidDataSource druidDataSource = (DruidDataSource) ds;
            // 失败后重连的次数
            druidDataSource.setConnectionErrorRetryAttempts(1);
            // 请求失败之后中断
            druidDataSource.setBreakAfterAcquireFailure(true);
            Connection connection = ds.getConnection();
            connection.close();
            return true;
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * 切换数据源
     *
     * @param key 数据库配置表主键
     */
    @Override
    public void changeDataSource(String key) {
        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
        if (!dynamicRoutingDataSource.getDataSources().containsKey(key)) {
            DynamicDataSourceContextHolder.push("master");
            RepositorySource repositorySource = this.getById(Long.valueOf(key));
            Assert.notNull(repositorySource, "根据主键{" + key + "}获取数据库配置信息失败，请检查！");
            DataSource dataSource = createDataSource(repositorySource);
            dynamicRoutingDataSource.addDataSource(key, dataSource);
        }
        DynamicDataSourceContextHolder.push(key);
    }

    /**
     * 获取数据库列表
     *
     * @param key 数据库配置表主键
     */
    @Override
    public List<LinkedHashMap<String, Object>> getSchemas(String key) {
        RepositorySource repositorySource = this.getById(Long.valueOf(key));
        RepositoryConfig repositoryConfig = repositoryConfigService.getByDb(repositorySource.getDbTypeName());
        changeDataSource(key);
        return execQuery(repositoryConfig.getSchemaSql());
    }

    /**
     * 获取数据库表
     *
     * @param key        数据库配置表主键
     * @param schemaName 表模式
     */
    @Override

    public List<LinkedHashMap<String, Object>> getTables(String key, String schemaName) {
        RepositorySource repositorySource = this.getById(Long.valueOf(key));
        RepositoryConfig repositoryConfig = repositoryConfigService.getByDb(repositorySource.getDbTypeName());
        Map<String, Object> map = new HashMap<>(1);
        map.put("schemaName", schemaName);
        String sql = repositoryConfig.getTableSql().replaceAll("#\\{schemaName}", schemaName);
        changeDataSource(key);
        return execQuery(sql);
    }

    /**
     * 获取数据库表字段
     *
     * @param key        数据库配置表主键
     * @param schemaName 表模式
     * @param tableName  表名称
     */
    @Override

    public List<LinkedHashMap<String, Object>> getColumns(String key, String schemaName, String tableName) {
        RepositorySource repositorySource = this.getById(Long.valueOf(key));
        RepositoryConfig repositoryConfig = repositoryConfigService.getByDb(repositorySource.getDbTypeName());
        String sql = repositoryConfig.getColumnSql().replaceAll("#\\{schemaName}", schemaName).replaceAll("#\\{tableName}", tableName);
        changeDataSource(key);
        return execQuery(sql);
    }

    /**
     * 获取数据库表主键
     *
     * @param key
     * @param schemaName
     * @param tableName
     * @return
     */
    @Override
    public List<LinkedHashMap<String, Object>> getPrimaryKey(String key, String schemaName, String tableName) {
        RepositorySource repositorySource = this.getById(Long.valueOf(key));
        RepositoryConfig repositoryConfig = repositoryConfigService.getByDb(repositorySource.getDbTypeName());
        String sql = repositoryConfig.getPrimaryKeySql().replaceAll("#\\{schemaName}", schemaName).replaceAll("#\\{tableName}", tableName);
        try {
            changeDataSource(key);
            return execQuery(sql);
        } finally {
            DynamicDataSourceContextHolder.poll();
            DynamicDataSourceContextHolder.clear();
        }
    }

    /**
     * 创建数据源
     *
     * @param repositorySource 数据源对象
     * @return 返回数据源
     */
    private DataSource createDataSource(RepositorySource repositorySource) {
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        BeanUtils.copyProperties(repositorySource, dataSourceProperty);
        return dataSourceCreator.createDataSource(dataSourceProperty);
    }


    private List<LinkedHashMap<String, Object>> execQuery(String sql) {
        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
        DataSource source = dynamicRoutingDataSource.getDataSource(DynamicDataSourceContextHolder.peek());
        // 创建RowMapper，将每一行映射为LinkedHashMap
        RowMapper<LinkedHashMap<String, Object>> rowMapper = (rs, rowNum) -> {
            LinkedHashMap<String, Object> result = new LinkedHashMap<>();
            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {
                String columnName = metaData.getColumnLabel(i);
                result.put(columnName, rs.getObject(columnName));
            }
            return result;
        };

        // 获取JdbcTemplate实例
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        return jdbcTemplate.query(sql, rowMapper);
    }

    private List<LinkedHashMap<String, Object>> execQuery2(String sql) {

        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
        DataSource source = dynamicRoutingDataSource.getDataSource(DynamicDataSourceContextHolder.peek());

        Connection connection = null;
        try {
            connection = source.getConnection();
            Statement preparedStatement = connection.createStatement();
            ResultSet resultSet = preparedStatement.executeQuery(sql);

            List<LinkedHashMap<String, Object>> list = new ArrayList<>();
            while (resultSet.next()) {
                LinkedHashMap<String, Object> linkedHashMap = new LinkedHashMap<>();
                ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = metaData.getColumnName(i);
                    Object value = resultSet.getObject(columnName);
                    linkedHashMap.put(columnName, value);
                }
                list.add(linkedHashMap);
            }
            return list;
        } catch (SQLException e) {
            log.error("执行查询失败,[" + sql + "]", e);
            throw new RuntimeException(e);
        } finally {
            try {
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                log.error("关闭数据库连接失败", e);
            }
        }
    }

    /**
     * 获取全部的连接信息
     *
     * @return
     */
    @Override
    public List<RepositorySource> list() {
        return repositorySourceMap.values().stream().toList();
    }

    @Override
    public void remove(Long id) {
        repositorySourceMap.remove(id);
    }
}




